package com.ale.oxetool.service;

import com.ale.oxetool.model.BatchSwitchTask;
import com.ale.oxetool.model.Phone;
import com.ale.oxetool.model.SwitchTask;
import com.ale.oxetool.util.SwitchTaskWorker;
import com.ale.oxetool.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.*;

/**
 * Created by guoshunw on 2016/4/6.
 */

@Service
public class SwitchTaskServiceImpl implements SwitchTaskService {

    @Value("${oxeserver.oxe1.host}")
    private String oxe1Host;
    @Value("${oxeserver.oxe2.host}")
    private String oxe2Host;

    @Value("${switch.prepare.maxtry}")
    private int maxRetry;

    @Value("${switch.prepare.delayBeforeSetOperation}")
    private int delayBeforeSetOperation;

    @Autowired
    private PhoneService phoneService;

    @Autowired
    private Util util;

    private static Logger log = LoggerFactory.getLogger(SwitchTaskServiceImpl.class);

    private ExecutorService executorService = Executors.newSingleThreadExecutor();

    @Value("${phone.reset.timeout}")
    private int phoneResetTimeout;

    @Value("${switch.batch.size}")
    private int switchBatchSize = -1;

    @Value("${switch.batch.interval}")
    private int switchBatchInterval = 5;

    public BatchSwitchTask startTask(List<String> requestedTaskIds) throws IOException {

        BatchSwitchTask batchSwitchTask = new BatchSwitchTask();
        batchSwitchTask.setOxe1Host(oxe1Host);
        batchSwitchTask.setOxe2Host(oxe2Host);

        List<Phone> phones = phoneService.getPhones();

        int maxRetryTimes = maxRetry;
        for (String requestedTaskId : requestedTaskIds) {
            String[] idAndTargetPlatform = requestedTaskId.split(";");
            Optional<Phone> optPhone = phones.stream().filter(ph -> ph.getMac().equals(idAndTargetPlatform[0])).findFirst();
            while (!optPhone.isPresent() && maxRetryTimes > 0) {
                log.warn("Requested switch task id {} doesn't found in phone list, planned in next round!", idAndTargetPlatform[0]);
                try {
                    TimeUnit.MICROSECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //Search phone again
                phones = phoneService.getPhones();
                optPhone = phones.stream().filter(ph -> ph.getMac().equals(idAndTargetPlatform[0])).findFirst();
            }
            if (!optPhone.isPresent()) {
                log.warn("Requested switch task id {} cann't be found after {} time search, give up switch.", idAndTargetPlatform[0], maxRetry);
                continue;
            }
            Phone phone = optPhone.get();
            int status = SwitchTask.NOT_START;
            if (util.isSwitchDone(phone, idAndTargetPlatform[1])) {
                status = SwitchTask.DONE;
            }
            SwitchTask task = new SwitchTask(phone, status, idAndTargetPlatform[1], "Normal switching...", phoneResetTimeout);
            batchSwitchTask.addTask(task);
        }
        batchSwitchTask.updateCurrentStatus();
        //start switch task
        SwitchTaskWorker worker = new SwitchTaskWorker(batchSwitchTask, phoneService, switchBatchSize, switchBatchInterval, delayBeforeSetOperation);
        Future<?> switchResult = executorService.submit(worker);

        log.info("switchBatchSize={}, switchBatchInterval={}, switchResult={}", switchBatchSize, switchBatchInterval, switchResult);
        //start refresh task
        executorService.submit(new SwitchRefreshTaskWorker(batchSwitchTask, phoneService, util));
        return batchSwitchTask;
    }

    private static class SwitchRefreshTaskWorker implements Callable<BatchSwitchTask> {
        private BatchSwitchTask batchSwitchTask;
        private PhoneService phoneService;
        private Util util;

        SwitchRefreshTaskWorker(BatchSwitchTask batchSwitchTask, PhoneService phoneService, Util util) {
            this.batchSwitchTask = batchSwitchTask;
            this.phoneService = phoneService;
            this.util = util;
        }

        @Override
        public BatchSwitchTask call() throws Exception {
            log.debug("=========================== Update batch task thread {} start =============================", Thread.currentThread().getName());
            int lastTimeDoneTaskNumber = 0;
            while (true) {
                //find task phones
//                log.debug("Refresh switch current remain task size: {}", batchSwitchTask.getStatusTasks(SwitchTask.IN_PROGRESS).size());
                List<SwitchTask> doneTasks = batchSwitchTask.updateSwitchStatus(phoneService.getPhones(), util);
                if (lastTimeDoneTaskNumber != doneTasks.size()) {
                    batchSwitchTask.updateCurrentStatus();
                    lastTimeDoneTaskNumber = doneTasks.size();
                }

                if (batchSwitchTask.isCompleted()) {
                    log.info("Batch switch task completed, so the update thread is about to exit now.");
                    break;
                }

                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("=========================== Update batch task thread {} exit =============================", Thread.currentThread().getName());
            return batchSwitchTask;
        }
    }
}
